package eu.liquify.ui {
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.InterpolationMethod;
	import flash.display.Shape;
	import flash.display.SpreadMethod;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.ui.Mouse;
	public class ColorPicker extends LabeledItem implements IHasBindValue {
		public var change:Function = NULL_FUNCTION;
		
		protected var swatch:ColorSwatch = new ColorSwatch();
		
		private var opened:Boolean = false;
		
		private var padding:Number = 5;
		private var picker:Panel;
		private var pickerOffset:Point = new Point();
		
		private var hueStrip:BitmapData;
		private var hueStripDisplay:Bitmap;
		private var hueStripIndicator:Shape = new Shape();
		private var hueStripHitbox:Sprite = new Sprite();
		private var hueStripHeight:int = 15;
		
		private var hue:Number;
		private var saturation:Number;
		private var value:Number;
		
		private var gradientSpacing:Number = 3;
		private var gradientIndicatorSize:Number = 10;
		private var gradient:Shape = new Shape();
		private var gradientIndicator:Shape = new Shape();
		private var gradientHitbox:Sprite = new Sprite();
		
		private var mouseHidden:Boolean = false;
		
		public function ColorPicker(options:Object = null) {
			item = swatch;
			swatch.addEventListener(Event.CHANGE, swatchChange);
			swatch.addEventListener(ColorSwatch.COLOR_UPDATE, swatchChange);
			swatch.addEventListener(Event.OPEN, togglePicker);
			addChild(swatch);
			
			picker = new Panel( { padding: padding, background: true });
			
			hueStripDisplay = new Bitmap();
			picker.addChild(hueStripDisplay);
			picker.addChild(hueStripIndicator);
			picker.addChild(hueStripHitbox);
			picker.addChild(gradient);
			picker.addChild(gradientIndicator);
			picker.addChild(gradientHitbox);
			
			hueStripHitbox.buttonMode = true;
			hueStripHitbox.addEventListener(MouseEvent.MOUSE_DOWN, hueDown);
			
			gradientHitbox.buttonMode = true;
			gradientHitbox.addEventListener(MouseEvent.MOUSE_DOWN, gradientDown);
			
			super();
			labelWidth = 40;
			itemWidth = 80;
			applyOptions(options);
		}
		
		public function get bindValue():Object { return color; }
		public function set bindValue(v:Object):void { color = uint(v) & 0x00FFFFFF; }
		
		private function updateChange():void {
			Utils.callDynamic(change, this);
			dispatchEvent(new ValueEvent(ValueEvent.VALUE_CHANGED, color));
		}
		
		public function get color():uint {
			return swatch.color;
		}
		public function set color(v:uint):void {
			swatch.color = v;
		}
		
		private function swatchChange(e:Event):void {
			updateChange();
			if (!opened) return;
			setHsvFromRGB(swatch.getInputColor());
			redrawHueStripIndicator();
			redrawGradient();
			redrawGradientIndicator();
		}
		
		private function updateColorFromHSV():void {
			swatch.color = hsvToRgb(hue, saturation, value);
		}
		
		private function togglePicker(e:Event):void {
			if (opened) {
				hidePicker();
			} else {
				showPicker();
			}
		}
		
		private function checkFocus(e:MouseEvent):void {
			var focused:Boolean = false;
			var cur:DisplayObject = e.target as DisplayObject;
			while (cur) {
				if (cur == this || cur == picker) {
					focused = true;
					break;
				}
				cur = cur.parent;
			}
			if (!focused) {
				hidePicker();
			}
		}
		
		private function hueDown(e:MouseEvent):void {
			stage.addEventListener(MouseEvent.MOUSE_UP, hueUp);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, hueMove);
			hueMove();
		}
		private function hueMove(e:MouseEvent = null):void {
			hue = Math.max(0, Math.min(1, hueStripHitbox.mouseX/hueStripHitbox.width));
			hue *= 360;
			redrawGradient();
			redrawHueStripIndicator();
			redrawGradientIndicator();
			updateColorFromHSV();
			updateChange();
		}
		private function hueUp(e:MouseEvent):void {
			stage.removeEventListener(MouseEvent.MOUSE_UP, hueUp);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, hueMove);
		}
		
		private function gradientDown(e:MouseEvent):void {
			stage.addEventListener(MouseEvent.MOUSE_UP, gradientUp);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, gradientMove);
			gradientMove();
		}
		private function gradientMove(e:MouseEvent = null):void {
			var x:Number = gradientHitbox.mouseX/gradientHitbox.width;
			var y:Number = gradientHitbox.mouseY/gradientHitbox.height;
			saturation = Math.max(0, Math.min(1, x));
			value = Math.max(0, Math.min(1, 1-y));
			redrawGradientIndicator();
			updateColorFromHSV();
			updateChange();
			
			// Hide after first move
			if (e) {
				if (x < 0 || x > 1 || y < 0 || y > 1) {
					if (mouseHidden) {
						Mouse.show();
						mouseHidden = false;
					}
				} else if (!mouseHidden) {
					Mouse.hide();
					mouseHidden = true;
				}
			}
		}
		private function gradientUp(e:MouseEvent):void {
			stage.removeEventListener(MouseEvent.MOUSE_UP, gradientUp);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, gradientMove);
			Mouse.show();
			mouseHidden = false;
		}
		
		private function showPicker():void {
			opened = true;
			TweenNano.killTweensOf(picker, true);
			
			stage.addEventListener(MouseEvent.MOUSE_DOWN, checkFocus);
			
			setHsvFromRGB(swatch.getInputColor());
			
			var p:Point = localToGlobal(pickerOffset);
			picker.x = p.x;
			picker.y = p.y;
			picker.alpha = 1;
			stage.addChild(picker);
			
			TweenNano.from(picker, 0.2, { alpha: 0, y: picker.y-10 } );
			
			resizePicker();
		}
		private function hidePicker():void {
			opened = false;
			TweenNano.killTweensOf(picker, true);
			
			stage.removeEventListener(MouseEvent.MOUSE_DOWN, checkFocus);
			
			TweenNano.to(picker, 0.2, { alpha: 0, y: picker.y-10, onComplete: destroyPicker } );
		}
		private function destroyPicker():void {
			stage.removeChild(picker);
			
			hueStrip.dispose();
			hueStrip = null;
			hueStripIndicator.graphics.clear();
			hueStripHitbox.graphics.clear();
			gradient.graphics.clear();
			gradientIndicator.graphics.clear();
			gradientHitbox.graphics.clear();
		}
		
		private function resizePicker():void {
			var i:int;
			
			var h:Number = 0;
			
			if (hueStrip) hueStrip.dispose();
			hueStrip = new BitmapData(width-padding*2, 1, false);
			for (i = 0; i < hueStrip.width; i++) {
				hueStrip.setPixel(i, 0, hsvToRgb(i/hueStrip.width*360, 1, 1));
			}
			//hueStripDisplay.x = padding;
			//hueStripDisplay.y = padding;
			hueStripDisplay.bitmapData = hueStrip;
			hueStripDisplay.scaleY = hueStripHeight;
			h += padding+hueStripHeight+gradientSpacing;
			redrawHueStripIndicator();
			redrawHueStripHitbox();
			
			gradient.x = gradientIndicator.x = gradientHitbox.x = 0;
			gradient.y = gradientIndicator.y = gradientHitbox.y = h;
			redrawGradient();
			h += width;
			redrawGradientIndicator();
			redrawGradientHitbox();
			
			picker.width = width;
			picker.height = h;
			picker.resize();
		}
		
		private function redrawHueStripHitbox():void {
			var g:Graphics = hueStripHitbox.graphics;
			g.clear();
			g.beginFill(0x000000, 0);
			g.drawRect(0, 0, width-padding*2, hueStripHeight);
		}
		
		private function redrawHueStripIndicator():void {
			var g:Graphics = hueStripIndicator.graphics;
			g.clear();
			g.lineStyle(0, 0x000000, 0.8);
			var x:Number = hue/360*hueStrip.width;
			g.moveTo(x, -2);
			g.lineTo(x, hueStripHeight*0.3);
			g.moveTo(x, hueStripHeight*0.7);
			g.lineTo(x, hueStripHeight+2);
		}
		
		private function redrawGradient():void {
			var w:Number = width-padding*2;
			var h:Number = width-padding*2;
			
			var saturatedColor:uint = hsvToRgb(hue, 1, 1);
			var g:Graphics = gradient.graphics;
			g.clear();
			var m:Matrix = new Matrix();
			
			g.beginFill(0xBEBEBE);
			g.drawRoundRect(-2, -2, w+4, h+4, 6, 6);
			
			m.createGradientBox(w, h);
			g.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, saturatedColor], [1, 1], [0, 255], m, SpreadMethod.PAD, InterpolationMethod.RGB);
			g.drawRect(0, 0, w, h);
			
			m.rotate(Math.PI/2);
			g.beginGradientFill(GradientType.LINEAR, [0x000000, 0x000000], [0, 1], [0, 255], m, SpreadMethod.PAD, InterpolationMethod.LINEAR_RGB);
			g.drawRect(0, 0, w, h);
		}
		private function redrawGradientIndicator():void {
			var w:Number = width-padding*2;
			var h:Number = width-padding*2;
			var x:Number = Math.round(saturation*w);
			var y:Number = Math.round((1-value)*h);
			
			var g:Graphics = gradientIndicator.graphics;
			g.clear();
			g.beginFill(0x000000);
			g.drawRect(x-gradientIndicatorSize/2, y-gradientIndicatorSize/2, gradientIndicatorSize, gradientIndicatorSize);
			g.drawRect(x-gradientIndicatorSize/2+2, y-gradientIndicatorSize/2+2, gradientIndicatorSize-4, gradientIndicatorSize-4);
			g.beginFill(0xFFFFFF);
			g.drawRect(x-gradientIndicatorSize/2+1, y-gradientIndicatorSize/2+1, gradientIndicatorSize-2, gradientIndicatorSize-2);
			g.drawRect(x-gradientIndicatorSize/2+2, y-gradientIndicatorSize/2+2, gradientIndicatorSize-4, gradientIndicatorSize-4);
			
			gradientIndicator.scrollRect = new Rectangle(0, 0, w, h);
			
			//g.beginFill(0x000000);
			//g.drawCircle(px, py, gradientIndicatorRadius);
			//g.beginFill(0xFFFFFF);
			//g.drawCircle(px, py, gradientIndicatorRadius-1);
			//g.drawCircle(px, py, gradientIndicatorRadius-2);
			//g.drawCircle(saturation*w, (1-value)*h, gradientIndicatorRadius);
		}
		private function redrawGradientHitbox():void {
			var g:Graphics = gradientHitbox.graphics;
			g.clear();
			g.beginFill(0x000000, 0);
			g.drawRect(0, 0, width-padding*2, width-padding*2);
		}
		
		private function hsvToRgb(h:Number, s:Number, v:Number):uint {
			var c:Number = v*s;
			var f:Number = h/60;
			var x:Number = c*(1-Math.abs(f%2-1));
			var r:Number, g:Number, b:Number;
			if (isNaN(h)) {
				r = g = b = 0;
			} else if (f < 1) {
				r = c; g = x; b = 0;
			} else if (f < 2) {
				r = x; g = c; b = 0;
			} else if (f < 3) {
				r = 0; g = c; b = x;
			} else if (f < 4) {
				r = 0; g = x; b = c;
			} else if (f < 5) {
				r = x; g = 0; b = c;
			} else {
				r = c; g = 0; b = x;
			}
			var m:Number = v-c;
			r += m; g += m; b += m;
			r = r < 0 ? 0 : (r > 1 ? 1 : r);
			g = g < 0 ? 0 : (g > 1 ? 1 : g);
			b = b < 0 ? 0 : (b > 1 ? 1 : b);
			return (r*0xFF << 16) | (g*0xFF << 8) | b*0xFF;
		}
		private function setHsvFromRGB(color:uint):void {
			var r:Number = ((color & 0xFF0000) >> 16)/0xFF;
			var g:Number = ((color & 0x00FF00) >> 8)/0xFF;
			var b:Number = (color & 0x0000FF)/0xFF; 
			var M:Number = Math.max(r, g, b);
			var m:Number = Math.min(r, g, b);
			var c:Number = M-m;
			var f:Number;
			if (c == 0) {
				f = NaN;
			} else if (M == r) {
				f = ((g-b)/c)%6;
			} else if (M == g) {
				f = (b-r)/c+2;
			} else if (M == b) {
				f = ((r-g)/c)+4;
			}
			hue = 60*f;
			value = M;
			saturation = c == 0 ? 0 : c/value;
			
			hue %= 360;
			if (hue < 0) hue += 360;
			value = value < 0 ? 0 : (value > 1 ? 1 : value);
			saturation = saturation < 0 ? 0 : (saturation > 1 ? 1 : saturation);
		}
		
		override public function resize():void {
			swatch.fixedSize = fixedSize;
			super.resize();
			swatch.height = height;
			swatch.resize();
			pickerOffset.y = height+3;
			resizePicker();
		}
		
	}

}